﻿using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;

namespace GypeInject
{
    public class Win32
    {
        public enum AllocationType : uint
        {
            COMMIT      = 0x00001000,
            RESERVE     = 0x00002000,
            RESET       = 0x00080000,
            RESET_UNDO  = 0x1000000,
            LARGE_PAGES = 0x20000000,
            PHYSICAL    = 0x00400000,
            TOP_DOWN    = 0x00100000
        }

        public enum DeallocationType : uint
        {
            DECOMMIT    = 0x4000,
            RELEASE     = 0x8000
        }

        public enum ProtectionFlags : uint
        {
            EXECUTE            = 0x010,
            EXECUTE_READ       = 0x020,
            EXECUTE_READWRITE  = 0x040,
            EXECUTE_WRITECOPY  = 0x080,
            NOACCES            = 0x001,
            READONLY           = 0x002,
            READWRITE          = 0x004,
            WRITECOPY          = 0x008,
            GUARD              = 0x100,
            NOCACHE            = 0x200,
            WRITECOMBINE       = 0x400
        }

        public enum ProcessAccessRights : uint
        {
            TERMINATE                   = 0x00000001,
            CREATE_THREAD               = 0x00000002,
            SET_SESSIONID               = 0x00000004,
            VM_OPERATION                = 0x00000008,
            VM_READ                     = 0x00000010,
            VM_WRITE                    = 0x00000020,
            DUP_HANDLE                  = 0x00000040,
            CREATE_PROCESS              = 0x00000080,
            SET_QUOTA                   = 0x00000100,
            SET_INFORMATION             = 0x00000200,
            QUERY_INFORMATION           = 0x00000400,
            SUSPEND_RESUME              = 0x00000800,
            QUERY_LIMITED_INFORMATION   = 0x00001000,

            ALL_ACCESS = 0x001FFFFF
        }

        public enum CreationFlags : uint
        {
            RUN                                 = 0x00000000,
            CREATE_SUPSENDED                    = 0x00000004,
            STACK_SIZE_PARAM_IS_A_RESERVATION   = 0x00010000
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct ModuleEntry32
        {
            public int Size;
            public int ModuleId;
            public int ProcessId;
            public int GlblcntUsage;
            public int ProccntUsage;
            public IntPtr BaseAddress;
            public int BaseSize;
            public IntPtr Module;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst=256)]
            public string ModuleName;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
            public string ModulePath;
        }

        public enum SnapShotFlags : uint
        {
            INHERIT         = 0x80000000,
            SNAPALL         = 0x0000000F,
            SNAPHEAPLIST    = 0x00000001,
            SNAPMODULE      = 0x00000008,
            SNAPMODULE32    = 0x00000010,
            SNAPPROCESS     = 0x00000002,
            SNAPTHREAD      = 0x00000004
        }

        [DllImport("Kernel32.dll")]
        public static extern IntPtr VirtualAllocEx(IntPtr processHandle, IntPtr address, int size, AllocationType type, ProtectionFlags flags);

        [DllImport("Kernel32.dll")]
        public static extern bool WriteProcessMemory(IntPtr processHandle, IntPtr address, byte[] buffer, int size, out int numberOfBytesWritten);

        [DllImport("Kernel32.dll")]
        public static extern bool ReadProcessMemory(IntPtr processHandle, IntPtr address, byte[] buffer, int size, out int numberOfBytesRead);

        [DllImport("Kernel32.dll")]
        public static extern IntPtr OpenProcess(ProcessAccessRights desiredAccess, bool inheritHandle, int processId);

        [DllImport("Kernel32.dll")]
        public static extern IntPtr CreateRemoteThread(IntPtr processHandle, IntPtr gimmeDatNull, int stackSize, IntPtr startAddress, IntPtr parameters, CreationFlags flags, out uint threadId);

        [DllImport("Kernel32.dll")]
        public static extern IntPtr GetProcAddress(IntPtr moduleHandle, string processName);

        [DllImport("Kernel32.dll")]
        public static extern IntPtr GetModuleHandle(string moduleName);

        [DllImport("Kernel32.dll")]
        public static extern uint WaitForSingleObject(IntPtr handle, int milliseconds);

        [DllImport("Kernel32.dll")]
        public static extern bool VirtualFreeEx(IntPtr processHandle, IntPtr address, int size, DeallocationType type);

        [DllImport("Kernel32.dll")]
        public static extern IntPtr LoadLibrary(string libraryName);

        [DllImport("Kernel32.dll")]
        public static extern bool FreeLibrary(IntPtr module);

        [DllImport("Kernel32.dll")]
        public static extern uint GetLastError();

        [DllImport("User32.dll")]
        public static extern IntPtr GetForegroundWindow();

        [DllImport("User32.dll")]
        public static extern int GetWindowThreadProcessId(IntPtr hwnd, out int processId);

        [DllImport("Kernel32.dll")]
        public static extern IntPtr CreateToolhelp32Snapshot(SnapShotFlags flags, int processId);

        [DllImport("Kernel32.dll")]
        public static extern bool Module32First(IntPtr snapshot, ref ModuleEntry32 module);

        [DllImport("Kernel32.dll")]
        public static extern bool Module32Next(IntPtr snapshot, ref ModuleEntry32 module);

        [DllImport("Kernel32.dll")]
        public static extern bool IsWow64Process(IntPtr processHandle, out bool isWow64Process);

        [DllImport("Kernel32.dll")]
        public static extern bool CloseHandle(IntPtr handle); 

        public static List<ModuleEntry32> EnumProcessModules(int processId, Func<ModuleEntry32, bool> function = null)
        {
            List<ModuleEntry32> list = new List<ModuleEntry32>();

            var flags = SnapShotFlags.SNAPMODULE|SnapShotFlags.SNAPMODULE32;
            var snapshot = CreateToolhelp32Snapshot(flags, processId);

            if (snapshot == new IntPtr(-1))
                return list;

            ModuleEntry32 module = new ModuleEntry32();
            module.Size = Marshal.SizeOf(module);

            if (!Module32First(snapshot, ref module))
                return list;

            do
            {
                if (function != null && function(module))
                    break;
                list.Add(module);
            } while (Module32Next(snapshot, ref module));

            CloseHandle(snapshot);
            return list;
        }

        public static T Read<T>(IntPtr handle, IntPtr address) where T: struct
        {
            int size = Marshal.SizeOf(typeof(T));
            byte[] bytes = new byte[size];
            int bytesRead = 0;

            Win32.ReadProcessMemory(handle, address, bytes, size, out bytesRead);

            unsafe
            {
                fixed (byte* ptr = bytes)
                    return (T)Marshal.PtrToStructure((IntPtr)ptr, typeof(T));
            }
        }

        public static T ReadSome<T>(IntPtr handle, IntPtr address, int size)
        {
            byte[] bytes = new byte[size];
            int bytesRead = 0;

            Win32.ReadProcessMemory(handle, address, bytes, size, out bytesRead);

            unsafe
            {
                fixed (byte* ptr = bytes)
                    return (T)Marshal.PtrToStructure((IntPtr)ptr, typeof(T));
            }
        }

        public static string ReadString(IntPtr handle, IntPtr address)
        {
            StringBuilder sb = new StringBuilder();

            List<byte> bytes = new List<byte>();
            
            byte b = 0;
            do
            {
                b = Read<byte>(handle, address);
                bytes.Add(b);
            } while (b != 0);

            return Encoding.ASCII.GetString(bytes.ToArray());
        }
    }
}
